package com.jacky.javafx.graph;

import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.util.Pair;

import java.util.ArrayList;
import java.util.Set;

public class Animation {
    private static int sleepTime = 2000;

    public static void animateBFS(int source, int destination, ArrayList<ArrayList<Integer>> layers,
                                  ArrayList<Integer> reversePath, AnchorPane graphArea) {
        System.out.println("Animating BFS");
        new Thread(() -> {
            for (ArrayList<Integer> layer : layers) {
                for (Integer node : layer) {
                    Circle currNoe = (Circle) graphArea.lookup("#circle_" + node);
                    currNoe.setFill(Color.CRIMSON);
                    currNoe.setStroke(Color.BLACK);
                    currNoe.setStrokeWidth(2);
                }

                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                for (Integer node : layer) {
                    Circle currNode = (Circle) graphArea.lookup("#circle_" + node);
                    if (node != source && node != destination) {
                        currNode.setFill(Color.LIGHTSKYBLUE);
                        currNode.setStrokeWidth(0);
                    } else if (node == destination) {
                        currNode.setFill(Color.LIGHTGREEN);
                        currNode.setStrokeWidth(2);
                    } else {
                        currNode.setFill(Color.YELLOW);
                        currNode.setStrokeWidth(2);
                    }
                }


                for (int i = reversePath.size() - 2; i >= 0; i--) {
                    int curr = reversePath.get(i), pre = reversePath.get(i + 1);
                    Circle currNode = (Circle) graphArea.lookup("#circle_" + curr);
                    if (curr != source && curr != destination) {
                        currNode.setStroke(Color.ORANGE);
                    }
                    currNode.setStrokeWidth(2);

                    Line currEdge = (Line) graphArea.lookup("#line_" + curr + "_" + pre);
                    if (currEdge == null) {
                        currEdge = (Line) graphArea.lookup("#line_" + pre + "_" + curr);
                    }

                    if (currEdge != null) {
                        currEdge.setStroke(Color.ORANGE);
                    }

                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    public static void animateDFS(int source, int destination, ArrayList<Integer> layer, AnchorPane graphArea) {
        System.out.println("Animating DFS");
        new Thread(() -> {
            for (Integer node : layer) {
                System.out.println("node:" + node);
                Circle currNode = (Circle) graphArea.lookup("#circle_" + node);
                currNode.setFill(Color.CRIMSON);
                currNode.setStroke(Color.BLACK);
                currNode.setStrokeWidth(2);

                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                if (node == source) {
                    currNode.setFill(Color.YELLOW);
                } else if (node == destination) {
                    currNode.setFill(Color.LIGHTGREEN);
                } else {
                    currNode.setFill(Color.LIGHTSKYBLUE);
                    currNode.setStrokeWidth(0);
                }
            }
        }).start();
    }

    public static void animateComponents(ArrayList<ArrayList<Integer>> components, AnchorPane graphArea) {
        String[] colors = {"#f58231", "#ffe119", "#3cb44b", "#4363d8", "#e6194b", "#911eb4", "#46f0f0", "#f032e6",
                "#bcf60c", "#fabebe", "#008080", "#e6beff", "#9a6324", "#fffac8", "#800000", "#aaffc3", "#808000",
                "#ffd8b1", "#000075", "#808080", "#ffffff", "#000000"};
        for (int i = 0; i < components.size(); i++) {
            for (Integer node : components.get(i)) {
                Circle currNode = (Circle) graphArea.lookup("#circle_" + node);
                currNode.setFill(Paint.valueOf(colors[i]));
            }
        }
    }

    public static void animateDijkstra(int source, int destination, ArrayList<Integer> layer, ArrayList<Integer> reversePath, AnchorPane graphArea) {
        System.out.println("animating dijkstra");
        new Thread(() -> {
            for (Integer node : layer) {
                System.out.println("node:" + node);
                Circle currNode = (Circle) graphArea.lookup("#circle_" + node);

                currNode.setFill(Color.CRIMSON);
                currNode.setStroke(Color.BLACK);
                currNode.setStrokeWidth(2);

                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                if (node == source) {
                    currNode.setFill(Color.YELLOW);
                } else if (node == destination) {
                    currNode.setFill(Color.LIGHTGREEN);
                } else {
                    currNode.setFill(Color.LIGHTSKYBLUE);
                    currNode.setStrokeWidth(0);
                }
            }

            for (int i = reversePath.size() - 2; i >= 0; i--) {
                int curr = reversePath.get(i), pre = reversePath.get(i + 1);
                Circle currNode = (Circle) graphArea.lookup("#circle_" + curr);
                currNode.setStrokeWidth(2);

                Line currEdge = (Line) graphArea.lookup("#line_" + curr + "_" + pre);
                if (currEdge == null) {
                    currEdge = (Line) graphArea.lookup("#line_" + pre + "_" + curr);
                }
                if (currEdge != null) {
                    currEdge.setStroke(Color.ORANGE);
                }

                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public static void animateSpanningTree(ArrayList<Pair<Integer, Integer>> components, AnchorPane graphArea) {
        new Thread(() -> {
            for (Pair<Integer, Integer> edge : components) {
                Integer p1 = edge.getKey();
                Integer p2 = edge.getValue();
                Line currEdge = (Line) graphArea.lookup("#line_" + p1 + "_" + p2);
                if (currEdge == null) {
                    currEdge = (Line) graphArea.lookup("#line_" + p2 + "_" + p1);
                }

                if (currEdge != null) {
                    currEdge.setStroke(Color.ORANGE);
                }

                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public static void animateBridges(ArrayList<Pair<Integer, Integer>> bridges, AnchorPane graphArea) {
        new Thread(() -> {
            for (Pair<Integer, Integer> edge : bridges) {
                Integer point1 = edge.getKey();
                Integer point2 = edge.getValue();

                Line bridge = (Line) graphArea.lookup("line_" + point1 + "_" + point2);
                if (bridge == null) {
                    bridge = (Line) graphArea.lookup("line_" + point2 + "_" + point1);
                }
                if (bridge != null) {
                    bridge.setStroke(Color.RED);
                }

                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public static void animateArticulationPoints(Set<Integer> articulationPoints, AnchorPane graphArea) {
        new Thread(() -> {
            for (Integer node : articulationPoints) {
                Circle currNode = (Circle) graphArea.lookup("#circle_" + node);
                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public static void setSliderSpeed(int value) {
        Animation.sleepTime = value;
    }
}
